/*    
    OpenMheg - A free MHEG-5 library
    Copyright (C) 2008 Dharmesh Malam

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Core.Model.Actions;
using Core.Model.Actions.Elementary;
using Parser;
using Action = Core.Model.Actions.Action;

namespace XMHEGDecoder.Builders
{
    public class ActionBuilder
    {
        public static Action Build ( XmlElement node )
        {
            Action action = new Action();
            
            foreach ( XmlElement actionElement in node.GetElementsByTagName( "action" ) )
            {
                action.ElementaryActions.Add( BuildElementaryAction(actionElement.FirstChild) );
                                
            }

            return action;
        }

        private static ElementaryAction BuildElementaryAction ( XmlNode node )
        {
            ElementaryAction ret = null;

            switch ( node.Name )
            {
                case ( "activate" ):
                    Activate activate = new Activate();
                    activate.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = activate;
                    break;

                case ( "add" ):

                    Add add = new Add( GenericVariableBuilder.BuildGenericObjectRef( node ["target"] ) );
                    add.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    add.Operand = GenericVariableBuilder.BuildGenericInteger( node ["value"] );

                    ret = add;

                    break;

                case ( "add-item" ):
                    //AddItem addItem = new AddItem();
                    //addItem.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    //ret = addItem;
                    break;

                case ( "append" ):
                    Append append = new Append();
                    append.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    append.String = GenericVariableBuilder.BuildGenericOctetString( node ["append-value"] );
                    ret = append;
                    break;

                case ( "bring-to-front" ):
                    BringToFront bringToFront = new BringToFront();
                    bringToFront.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = bringToFront;
                    break;

                case ( "call" ):
                    Call call = new Call();
                    call.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    call.CallSucceeded = ObjectRefBuilder.Build( node ["succeeded"] );
                    

                    foreach ( XmlElement parameter in node ["parameters"] )
                    {
                        call.Parameters.Add( GenericVariableBuilder.Build( parameter ) );
                    }

                    ret = call;
                    break;

                case ( "call-action-slot" ):
                    CallActionSlot callActionSlot = new CallActionSlot();
                    callActionSlot.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    callActionSlot.Index = GenericVariableBuilder.BuildGenericInteger( node ["index"] );
                    ret = callActionSlot;
                    break;

                case ( "clear" ):
                    //Clear clear = new Clear();
                    //clear.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    //ret = clear;
                    break;

                case ( "clone" ):

                    break;

                case ( "deactivate" ):
                    Deactivate deactivate = new Deactivate();
                    deactivate.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = deactivate;
                    break;

                case ( "del-item" ):
                    //DelItem delItem = new DelItem();
                    //delItem.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    //ret = delItem;
                    break;

                case ( "deselect" ):
                    //not exists?
                    break;

                case ( "deselect-item" ):
                    //DeselectItem deselectItem = new DeselectItem();
                    //deselectItem.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    //ret = deselectItem;
                    break;

                case ( "divide" ):
    
                    Divide divide = new Divide();
                    divide.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    divide.Operand = GenericVariableBuilder.BuildGenericInteger( node ["value"] );
                    ret = divide;
                    
                    break;

                case ( "draw-arc" ):

                    break;
                case ( "draw-line" ):

                    break;
                case ( "draw-oval" ):

                    break;
                case ( "draw-polygon" ):

                    break;
                case ( "draw-polyline" ):

                    break;
                case ( "draw-rectangle" ):
                    DrawRectangle drawRectangle = new DrawRectangle();
                    drawRectangle.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    drawRectangle.X1 = GenericVariableBuilder.BuildGenericInteger( node ["x1"] );
                    drawRectangle.Y1 = GenericVariableBuilder.BuildGenericInteger( node ["y1"] );
                    drawRectangle.X2 = GenericVariableBuilder.BuildGenericInteger( node ["x2"] );
                    drawRectangle.Y2 = GenericVariableBuilder.BuildGenericInteger( node ["y2"] );
                    ret = drawRectangle;

                    break;
                case ( "draw-sector" ):

                    break;
                case ( "fork" ):
                    //Fork fork = new Fork();
                    //fork.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    //ret = fork;
                    break;

                case ( "get-availability-status" ):

                    break;
                case ( "get-box-size" ):

                    break;
                case ( "get-cell-item" ):

                    break;
                case ( "get-cursor-position" ):

                    break;
                case ( "get-engine-support" ):
                    GetEngineSupport getEngineSupport = new GetEngineSupport();
                    getEngineSupport.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    getEngineSupport.Feature = GenericVariableBuilder.BuildGenericOctetString(node ["feature"]);
                    getEngineSupport.Answer = ObjectRefBuilder.Build( node ["answer"] );
                    ret = getEngineSupport;

                    break;
                case ( "get-entry-point" ):

                    break;
                case ( "get-fill-colour" ):

                    break;
                case ( "get-first-item" ):

                    break;
                case ( "get-highlight-status" ):

                    break;
                case ( "get-interaction-status" ):

                    break;
                case ( "get-item-status" ):

                    break;
                case ( "get-label" ):

                    break;
                case ( "get-last-anchor-fired" ):

                    break;
                case ( "get-line-colour" ):

                    break;
                case ( "get-line-style" ):

                    break;
                case ( "get-line-width" ):

                    break;
                case ( "get-list-item" ):

                    break;
                case ( "get-list-size" ):

                    break;
                case ( "get-overwrite-mode" ):

                    break;
                case ( "get-portion" ):

                    break;
                case ( "get-position" ):

                    break;
                case ( "get-running-status" ):
                    GetRunningStatus getRunningStatus = new GetRunningStatus();
                    getRunningStatus.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    getRunningStatus.RunningStatus = ObjectRefBuilder.Build( node ["var"] );
                    ret = getRunningStatus;
                    
                    break;
                case ( "get-selection-status" ):

                    break;
                case ( "get-slider-value" ):

                    break;
                case ( "get-text-content" ):

                    break;
                case ( "get-text-data" ):
                    GetTextData getTextData = new GetTextData();
                    getTextData.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    getTextData.TextDataVar = ObjectRefBuilder.Build( node ["var"] );
                    ret = getTextData;
                    break;
                case ( "get-token-position" ):

                    break;
                case ( "get-volume" ):

                    break;
                case ( "launch" ):
                    Launch launch = new Launch();
                    launch.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node[ "target" ] );
                    ret = launch;
                    break;
                case ( "lock-screen" ):

                    LockScreen lockScreen = new LockScreen();
                    lockScreen.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = lockScreen;
                    break;

                case ( "modulo" ):
                    Modulo modulo = new Modulo();
                    modulo.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    modulo.Operand = GenericVariableBuilder.BuildGenericInteger( node ["value"] );
                    ret = modulo;

                    break;
                case ( "move" ):
                    Move move = new Move();
                    move.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    move.MovementId = GenericVariableBuilder.BuildGenericInteger( node ["value"] );
                    ret = move;

                    break;

                case ( "move-to" ):
                    MoveTo moveTo = new MoveTo();
                    moveTo.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    moveTo.Index = GenericVariableBuilder.BuildGenericInteger( node ["value"] );
                    ret = moveTo;
                    break;

                case ( "multiply" ):
                    
                    Multiply multiply = new Multiply();
                    multiply.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    multiply.Operand = GenericVariableBuilder.BuildGenericInteger( node ["value"] );
                    ret = multiply;
                    break;

                case ( "preload" ):

                    Preload preload = new Preload();
                    preload.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node[ "target" ] );
                    ret = preload;
                    break;
                case ( "put-before" ):
                    PutBefore putBefore = new PutBefore();
                    putBefore.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    putBefore.Visible = GenericVariableBuilder.BuildGenericObjectRef( node ["visible-reference"] );
                    ret = putBefore;
                    break;
                case ( "put-behind" ):

                    PutBehind putBehind = new PutBehind();
                    putBehind.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    putBehind.Visible = GenericVariableBuilder.BuildGenericObjectRef( node ["visible-reference"] );
                    ret = putBehind;

                    break;

                case ( "quit" ):

                    Quit quit = new Quit();
                    quit.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node[ "target" ] );
                    ret = quit;
                    break;
                case ( "read-persistent" ):
                    ReadPersistent readPersistent = new ReadPersistent();
                    readPersistent.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );

                    readPersistent.ReadSucceeded= ObjectRefBuilder.Build( node ["succeeded"] );
                    
                    foreach( XmlElement var in node["variables"])
                    {
                        readPersistent.Variables.Add( ObjectRefBuilder.Build( var ) );

                    }
                    
                    readPersistent.Input = GenericVariableBuilder.BuildGenericOctetString( node ["file-name"] );

                    ret = readPersistent;

                    break;
                case ( "run" ):
                    RunAction run = new RunAction();
                    run.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = run;
                    break;
                case ( "scale-bitmap" ):
                    ScaleBitmap scaleBitmap = new ScaleBitmap();
                    scaleBitmap.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    scaleBitmap.XScale = GenericVariableBuilder.BuildGenericInteger( node ["x"] );
                    scaleBitmap.YScale = GenericVariableBuilder.BuildGenericInteger( node ["y"] );

                    ret = scaleBitmap;
                    break;
                case ( "scale-video" ):
                    ScaleVideo scaleVideo = new ScaleVideo();
                    scaleVideo.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    scaleVideo.XScale = GenericVariableBuilder.BuildGenericInteger(node["x"]);
                    scaleVideo.YScale = GenericVariableBuilder.BuildGenericInteger( node ["y"] );

                    ret = scaleVideo;
                    break;
                case ( "scroll-items" ):

                    break;
                case ( "select" ):

                    //Select select = new Select();
                    //select.TargetRef = GenericVariableBuilder.BuildGenericObjectRef(node["target"]);
                    //ret = select;

                    break;
                case ( "select-item" ):

                    break;
                case ( "send-event" ):
                    SendEvent sendEvent = new SendEvent();
                    sendEvent.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    sendEvent.EventSource = GenericVariableBuilder.BuildGenericObjectRef( node ["emulated-event-source"] );
                    sendEvent.EventType = Core.Model.Events.Event.getEvent( DecodeUtils.ParseInt( node ["emulated-event-type"].InnerText ));
                    sendEvent.EventData = GenericVariableBuilder.Build( (XmlElement)(node ["emulated-event-data"]).FirstChild);
                    ret = sendEvent;
                    break;
                case ( "send-to-back" ):

                    SendToBack sendToBack = new SendToBack();
                    sendToBack.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = sendToBack;
                    break;
                case ( "set-box-size" ):
                    SetBoxSize setBoxSize = new SetBoxSize();
                    setBoxSize.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    setBoxSize.Width = GenericVariableBuilder.BuildGenericInteger( node ["x"] );
                    setBoxSize.Height = GenericVariableBuilder.BuildGenericInteger( node ["y"] );
                    ret = setBoxSize;
                    break;
                case ( "set-cache-priority" ):

                    break;
                case ( "set-counter-end-position" ):

                    break;
                case ( "set-counter-position" ):

                    break;
                case ( "set-counter-trigger" ):

                    break;
                case ( "set-cursor-position" ):

                    break;
                case ( "set-cursor-shape" ):

                    break;
                case ( "set-data" ):
                    SetData setData = new SetData();
                    setData.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );

                    XmlElement indirect, octetString;

                    if ( ( indirect = node ["content"] ["new-referenced-content"] ) != null )
                        setData.NewReferencedContent = GenericVariableBuilder.BuildGenericContentRef( indirect ["GenericContentReference"] );
                    else if ( ( octetString = node ["content"] ["new-included-content"] ) != null )
                        setData.NewIncludedContent = GenericVariableBuilder.BuildGenericOctetString( octetString );
                    else
                        throw new Exception( "Unkown nodes in set-data" );
                    ret = setData;

                    break;
                case ( "set-entry-point" ):

                    break;
                case ( "set-fill-colour" ):

                    SetFillColour setFillColour= new SetFillColour();
                    setFillColour.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    setFillColour.FillColour = ColourBuilder.Build( node ["colour"] );

                    ret = setFillColour;
                    break;

                case ( "set-first-item" ):

                    break;
                case ( "set-font-ref" ):

                    break;
                case ( "set-highlight-status" ):

                    break;
                case ("set-input-register"):
                    break;

                case ( "set-interaction-status" ):

                    break;
                case ( "set-label" ):

                    break;
                case ( "set-line-colour" ):
                    SetLineColour setLineColour = new SetLineColour();
                    setLineColour.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    setLineColour.LineColour = ColourBuilder.Build( node ["colour"] );

                    ret = setLineColour;

                    break;
                case ( "set-line-style" ):

                    break;
                case ( "set-line-width" ):

                    break;
                case ( "set-overwrite-mode" ):

                    break;
                case ( "set-palette-ref" ):

                    break;
                case ( "set-portion" ):

                    break;

                case ( "set-position" ):

                    SetPosition setPosition = new SetPosition();
                    setPosition.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    setPosition.NewX = GenericVariableBuilder.BuildGenericInteger( node ["x"] );
                    setPosition.NewY = GenericVariableBuilder.BuildGenericInteger( node ["y"] );

                    ret = setPosition;
                    break;

                case ( "set-slider-value" ):

                    break;
                
                case ( "set-speed" ):

                    break;

                case ( "set-timer" ):

                    SetTimer setTimer= new SetTimer();
                    setTimer.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    setTimer.TimerId = GenericVariableBuilder.BuildGenericInteger( node ["value"] );

                    if ( node ["timer"] != null )
                    {
                        if ( node ["timer"] ["timer-value"] != null )
                            setTimer.TimerValue = GenericVariableBuilder.BuildGenericInteger( node ["timer"] ["timer-value"] );

                        if ( node ["timer"] ["absolute-time"] != null )
                            setTimer.AbsoluteTime = GenericVariableBuilder.BuildGenericBoolean( node ["timer"] ["absolute-time"] );
                    }
                    ret = setTimer;

                    break;
                    
                case ( "set-transparency" ):

                    break;

                case ( "set-variable" ):

                    SetVariable setVariable = new SetVariable();
                    setVariable.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    BuildActionParameter( setVariable, node ["new-variable-value"] );
                    ret = setVariable;
                    break;

                case ( "set-volume" ):

                    break;

                case ( "spawn" ):

                    /*
                      Spawn spawn = new Spawn();
                      spawn.targetRef = ObjectRefBuilder.buildGeneric(node.getChild("target"));
                      ret = spawn;
                    */
                    break;
                case ( "step" ):

                    break;
                case ( "stop" ):
                    Stop stop = new Stop();
                    stop.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = stop;
                    break;

                case ( "store-persistent" ):
                    StorePersistent storePersistent = new StorePersistent();
                    storePersistent.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    storePersistent.StoreSucceeded = ObjectRefBuilder.Build( node ["succeeded"] );
                    //todo
                    foreach ( XmlElement var in node ["variables"] )
                    {
                        storePersistent.Variables.Add( ObjectRefBuilder.Build( var ) );

                    }
                    storePersistent.Output = GenericVariableBuilder.BuildGenericOctetString( node ["file-name"] );
                    ret = storePersistent;

                    break;
                case ( "subtract" ):

                    Subtract subtract = new Subtract( GenericVariableBuilder.BuildGenericObjectRef( node ["target"] ) );
                    subtract.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    subtract.Operand = GenericVariableBuilder.BuildGenericInteger( node ["value"] );

                    ret = subtract;

                    break;
                case ( "test-variable" ):

                    TestVariable testVariable = new TestVariable();
                    testVariable.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    testVariable.Operator = GenericVariableBuilder.BuildGenericInteger( node ["operator"] );

                    BuildActionParameter( testVariable, node ["operand"] );
                    ret = testVariable;

                    break;
                case ( "toggle" ):

                    //Toggle toggle = new Toggle();
                    //toggle.TargetRef = GenericVariableBuilder.BuildGenericObjectRef(node["target"]);
                    //ret = toggle;

                    break;
                case ( "toggle-item" ):

                    break;
                case ( "transition-to" ):

                    TransitionTo transitionTo = new TransitionTo();
                    transitionTo.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    //node["connection-tag-or-null"]
                    ret = transitionTo;

                    break;
                case ( "unload" ):

                    Unload unload = new Unload();
                    unload.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target" ] );
                    ret = unload;
                    break;
                case ( "unlock-screen" ):
                    UnlockScreen unlockScreen = new UnlockScreen();
                    unlockScreen.TargetRef = GenericVariableBuilder.BuildGenericObjectRef( node ["target"] );
                    ret = unlockScreen;
                    break;

                default:
                    ret = null;
                    break;
            }

            if ( ret == null )
            {
                node = node;
               // System.Diagnostics.Debug.WriteLine( "Unkown Action : "+node.Name );
                throw new Exception( "Unkown Action : "+node.Name );
            }

            return ret;
        }

        private static void BuildActionParameter ( ParameterAction parameterAction, XmlElement node )
        {
            XmlElement octetString, integer, boolean, contentRef, objectRef;

            if ( ( octetString = node ["new-generic-octetstring"] )!= null )
            {
                parameterAction.StringVal = GenericVariableBuilder.BuildGenericOctetString( octetString );
            }
            else if ( ( integer = node ["new-generic-integer"] )!= null )
            {
                parameterAction.IntVal = GenericVariableBuilder.BuildGenericInteger( integer );
            }
            else if ( ( boolean = node ["new-generic-boolean"] )!= null )
            {
                parameterAction.BoolVal = GenericVariableBuilder.BuildGenericBoolean( boolean );
            }
            else if ( ( contentRef = node ["new-generic-content-reference"] )!= null )
            {
                parameterAction.ContentRefVal = GenericVariableBuilder.BuildGenericContentRef( contentRef );
            }
            else if ( ( objectRef = node ["new-generic-object-reference"] )!= null )
            {
                parameterAction.ObjectRefVal = GenericVariableBuilder.BuildGenericObjectRef( objectRef );
            }
            else
                throw new Exception( "Unkown parameter" );
        }
    }
}
